package com.kamistoat.ruoyi.ruoyicommoncore.context;

import com.alibaba.ttl.TransmittableThreadLocal;
import com.kamistoat.ruoyi.ruoyicommoncore.constant.SecurityConstants;
import com.kamistoat.ruoyi.ruoyicommoncore.text.Convert;
import com.kamistoat.ruoyi.ruoyicommoncore.utils.StringUtils;


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * SecurityContextHolder. 用于将Security机制和 ThreadLocal配合使用。
 * 当初在商城项目中，我们用一个拦截器，每次请求一来，将请求中用户登录信息提取出来，然后放在ThreadLocal中。
 * 该类就是专门抽取出来，提供操作ThreadLocal的接口。
 * 而提取用户登录信息的拦截器并放到 ThreadLocal中，则由 ruoyi-common-security模块中的 HeaderInterceptor拦截器实现。
 * <p>
 * 提供 set / get 当前线程变量中的 用户id、用户名称、用户Token 的相关接口。
 */
public class SecurityContextHolder {
    // 使用阿里巴巴事务线程池，而非库类 ThreadLocal
    // 只不过这个线程池的使用方法其实和正常的 ThreadLocal很相似。同样直接使用get/set直接获取，ThreadLocal本身是不需要传入key的
    // 因此为了实现在 ThreadLocal中存放kv的目的，直接把ThreadLocal本身的值设置为 Map
    private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>();

    /**
     * 向 ThreadLocal中的 map中存放 kv对
     *
     * @param key
     * @param value
     */
    public static void set(String key, Object value) {
        Map<String, Object> localMap = getLocalMap();
        localMap.put(key, value == null ? StringUtils.EMPTY : value);
    }

    /**
     * 从 ThreadLocal中的 map中根据 key提取出 object，将object转换为String
     * 使用 Convert 中的静态方法 toStr进行转换
     *
     * @param key
     * @return
     */
    public static String get(String key) {
        Map<String, Object> localMap = getLocalMap();
        return Convert.toStr(localMap.getOrDefault(key, StringUtils.EMPTY));
    }

    /**
     * 从 ThreadLocal中的 map中根据 key提取出 object
     * 将 object转换为其原本的类进行返回
     *
     * @param key
     * @param <T>
     * @return
     */
    public static <T> T get(String key, Class<T> clazz) {
        Map<String, Object> localMap = getLocalMap();
        return StringUtils.cast(localMap.getOrDefault(key, null));
    }

    /**
     * 从 ThreadLocal中的 map中根据 key提取出 object
     * 将 object转换为指定的 clazz
     *
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> Class<?> getToTarget(String key, Class<T> clazz) {
        Map<String, Object> localMap = getLocalMap();
        return StringUtils.cast(localMap.getOrDefault(key, null), clazz);
    }


    /**
     * 从 ThreadLocal中获取值
     *
     * @return
     */
    public static Map<String, Object> getLocalMap() {
        Map<String, Object> stringObjectMap = THREAD_LOCAL.get();
        if (stringObjectMap == null) {
            // 注意，使用的是 ConcurrentHashMap，相比与普通的HashMap，在多线程下的安全性更高，不会出现成环的问题
            stringObjectMap = new ConcurrentHashMap<String, Object>();
            THREAD_LOCAL.set(stringObjectMap);
        }
        return stringObjectMap;
    }

    /**
     * 设置 ThreadLocal的值
     *
     * @param threadLocalMap
     */
    public static void setLocalMap(Map<String, Object> threadLocalMap) {
        THREAD_LOCAL.set(threadLocalMap);
    }


    /**
     * 从ThreadLocal中的 map中获取用户id
     */
    public static Long getUserId() {
        return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L);
    }

    public static void setUserId(String account) {
        set(SecurityConstants.DETAILS_USER_ID, account);
    }

    /**
     * 从ThreadLocal中的 map中获取用户name
     */
    public static String getUserName() {
        return get(SecurityConstants.DETAILS_USERNAME);
    }

    public static void setUserName(String username) {
        set(SecurityConstants.DETAILS_USERNAME, username);
    }

    /**
     * 从ThreadLocal中的 map中获取用户key
     */
    public static String getUserKey() {
        return get(SecurityConstants.USER_KEY);
    }

    public static void setUserKey(String userKey) {
        set(SecurityConstants.USER_KEY, userKey);
    }

    /**
     * 清空 ThreadLocal中当前存储的内容
     */
    public static void remove() {
        THREAD_LOCAL.remove();
    }

}
